package com.nokia.example.imagescaler;

import javax.microedition.lcdui.Image;

public class FixedPointBilinearInterpolationScaler extends ImageScaler {
	private static final int FP_SHIFT = 12;

	/**
	 * From ImageScaler.
	 */
	public Image scale(final int newWidth, final int newHeight,
			final Image originalImage) {
		if (newWidth <= 0 || newHeight <= 0) {
			throw new IllegalArgumentException(
					"Invalid scale new width or height! (" + newWidth + ", "
							+ newHeight + ")");
		}
		if (originalImage == null) {
			throw new IllegalArgumentException("Image to scale is null!");
		}

		// Get the size and the RGB array of the original image
		final int sourceWidth = originalImage.getWidth();
		final int sourceHeight = originalImage.getHeight();
		final int[] originalRgb = new int[sourceWidth * sourceHeight];
		originalImage.getRGB(originalRgb, 0, sourceWidth, 0, 0, sourceWidth,
				sourceHeight);

		final int xRatioFP = float2fp((float)(sourceWidth - 1) / newWidth);
		final int yRatioFP = float2fp((float)(sourceHeight - 1) / newHeight);
		final int[] scaledRgb = new int[newWidth * newHeight];
		int offset = 0;
		
		for (int i = 0; i < newHeight; ++i) {
			for (int j = 0; j < newWidth; ++j) {
				final int xFP = xRatioFP * j;
				final int yFP = yRatioFP * i;
				final int xDiffFP = xRatioFP * j - xFP;
				final int yDiffFP = yRatioFP * i - yFP;
				final int index = fp2int(yFP) * sourceWidth + fp2int(xFP);
				final int a = originalRgb[index];
				final int b = originalRgb[index + 1];
				final int c = originalRgb[index + sourceWidth];
				final int d = originalRgb[index + sourceWidth + 1];

				final int alpha = ((a >> 24) & 0xff)
						* fp2int(fp2int((int2fp(1) - xDiffFP) * int2fp(1)
								- yDiffFP)
								+ ((b >> 24) & 0xff)
								* fp2int(fp2int(xDiffFP * int2fp(1) - yDiffFP))
								+ ((c >> 24) & 0xff)
								* fp2int(fp2int(yDiffFP * (int2fp(1) - xDiffFP)))
								+ ((d >> 24) & 0xff)
								* fp2int(fp2int((xDiffFP * yDiffFP))));

				final int red = ((a >> 16) & 0xff)
						* fp2int(fp2int((int2fp(1) - xDiffFP)
								* (int2fp(1) - yDiffFP))
								+ ((b >> 16) & 0xff)
								* fp2int(fp2int(xDiffFP * (int2fp(1) - yDiffFP)))
								+ ((c >> 16) & 0xff)
								* fp2int(fp2int(yDiffFP * (int2fp(1) - xDiffFP)))
								+ ((d >> 16) & 0xff)
								* fp2int(fp2int(xDiffFP * yDiffFP)));

				final int green = ((a >> 8) & 0xff)
						* fp2int(fp2int((int2fp(1) - xDiffFP)
								* (int2fp(1) - yDiffFP))) + ((b >> 8) & 0xff)
						* fp2int(fp2int(xDiffFP * (int2fp(1) - yDiffFP)))
						+ ((c >> 8) & 0xff)
						* fp2int(fp2int(yDiffFP * (int2fp(1) - xDiffFP)))
						+ ((d >> 8) & 0xff)
						* fp2int(fp2int((xDiffFP * yDiffFP)));

				final int blue = (a & 0xff)
						* fp2int(fp2int((int2fp(1) - xDiffFP)
								* (int2fp(1) - yDiffFP))) + (b & 0xff)
						* fp2int(fp2int(xDiffFP * (int2fp(1) - yDiffFP)))
						+ (c & 0xff)
						* fp2int(fp2int(yDiffFP * (int2fp(1) - xDiffFP)))
						+ (d & 0xff) * fp2int(fp2int((xDiffFP * yDiffFP)));

				scaledRgb[offset++] = (alpha << 24) & 0xff000000 | (red << 16)
						& 0xff0000 | (green << 8) & 0xff00 | blue;
			}
		}

		return Image.createRGBImage(scaledRgb, newWidth, newHeight, true);
	}

	/**
	 * 
	 * @param i
	 * @return
	 */
	private int int2fp(final int i) {
		return i << FP_SHIFT;
	}

	/**
	 * 
	 * @param i
	 * @return
	 */
	private int fp2int(final int i) {
		return i >>> FP_SHIFT;
	}

	/**
	 * 
	 * @param f
	 * @return
	 */
	private static int float2fp(final float f) {
		return (int) (f * (1 << FP_SHIFT));
	}


	/**
	 * From ImageScaler.
	 */
	public String type() {
		return "FixedPoint Bilinear interpolation";
	}
}
